﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using AAA.Meta.Quote.Data;

namespace AAA.Meta.Chart.Data
{
    public class ProfileMgr
    {
        private DateTime _startTime;
        private DateTime _endTime;
        private SymbolBaseInfo _symSymbolInfo;
        private int _iTimePeriod = 15 * 60;
        private float _fPriceInterval = 5;
        private List<DateTime> _lstProfile;
        private Dictionary<DateTime, ProfileData> _dicProfile;
        private List<float> _lstCellPrice = new List<float>();
        private float AxisPriceHeight = -float.MaxValue;
        private float AxisPriceLow = float.MaxValue;
        //Profile Chart Data
        private Dictionary<float, Dictionary<float, ProfileCharData>> _dicProfileData = new Dictionary<float, Dictionary<float, ProfileCharData>>();
        //VT Chart Data
        private float fAxisXVolMin = 0;
        private float fAxisXVolMax = 1;
        private float fAxisXTicksMin = 0;
        private float fAxisXTicksMax = 1; private Dictionary<float, Dictionary<int, float>> _dicVolData = new Dictionary<float, Dictionary<int, float>>();
        private Dictionary<float, List<int>> _dicVolSeq = new Dictionary<float, List<int>>();
        private Dictionary<float, Dictionary<int, float>> _dicTicksData = new Dictionary<float, Dictionary<int, float>>();
        private Dictionary<float, List<int>> _dicTicksSeq = new Dictionary<float, List<int>>();

        public ProfileMgr()
        {
            _startTime = DateTime.Parse(DateTime.Now.ToString("yyyy/MM/dd") + " 08:45:01");
            _endTime = DateTime.Parse(DateTime.Now.ToString("yyyy/MM/dd") + " 13:45:00");
            _lstProfile = new List<DateTime>();
            _dicProfile = new Dictionary<DateTime, ProfileData>();
        }

        public ProfileMgr(SymbolBaseInfo SymbolInfoData)
        {
            _lstProfile = new List<DateTime>();
            _dicProfile = new Dictionary<DateTime, ProfileData>();
            _symSymbolInfo = SymbolInfoData;
            _startTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy/MM/dd") + " " + _symSymbolInfo.Start);
            _endTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy/MM/dd") + " " + _symSymbolInfo.End);
        }

        public DateTime StartTime
        {
            get { return _startTime; }
            set { _startTime = value; }
        }

        public DateTime EndTime
        {
            get { return _endTime; }
            set { _endTime = value; }
        }

        public float PriceInterval
        {
            get { return _fPriceInterval; }
            set { _fPriceInterval = value; }
        }

        public int TimePeriod
        {
            get { return _iTimePeriod; }
            set { _iTimePeriod = value; }
        }

        public void AddData(DateTime dtTime, float fPrice, int iVolume)
        {
            AddData(dtTime, fPrice, iVolume, 1);
        }

        public void AddData(DateTime dtTime, float fPrice, int iVolume, int iTick)
        {
            try
            {
                if (string.Compare(dtTime.ToString("HH:mm:ss"), _symSymbolInfo.Start) < 0)
                {
                    dtTime = Convert.ToDateTime(dtTime.ToString("yyyy/MM/dd") + " " + _symSymbolInfo.Start);
                } 
                if ((_lstProfile.Count == 0) || 
                    (_dicProfile[_lstProfile[_lstProfile.Count - 1]].EndTime.ToString("yyyy/MM/dd") != dtTime.ToString("yyyy/MM/dd")))
                {
                    ProfileData newProfile = new ProfileData();
                    newProfile.PriceInterval = PriceInterval;
                    newProfile.TimePeriod = TimePeriod;
                    newProfile.StartTime = DateTime.Parse(dtTime.ToString("yyyy/MM/dd") + " " + _symSymbolInfo.Start);
                    if (_startTime > newProfile.StartTime) {
                        _startTime = newProfile.StartTime;
                    }
                    _dicProfile.Add(newProfile.StartTime, newProfile);
                    _lstProfile.Add(newProfile.StartTime);
                }
                //將超出時間的TICK歸回最後一個Period
                if (string.Compare(dtTime.ToString("HH:mm:ss"), _symSymbolInfo.End) > 0)
                {
                    dtTime = Convert.ToDateTime(dtTime.ToString("yyyy/MM/dd") + " " + _symSymbolInfo.End);
                } 
                _dicProfile[_lstProfile[_lstProfile.Count - 1]].AddData(dtTime, fPrice, iVolume, iTick);
                _lstProfile.Sort();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
        }

        public void AddHistoryData(DateTime dtTime, float fPrice, int iVolume, int iTick)
        {
            try
            {
                dtTime = dtTime.AddSeconds(-59);
                AddData(dtTime, fPrice, iVolume, iTick);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
        }

        public void Split(ProfileData profile, DateTime profileIndex) {
            ProfileData rProfile = _dicProfile[profile.StartTime].Split(profileIndex);
            if (_dicProfile[profile.StartTime].PriceList.Count == 0)
            {
                _lstProfile.Remove(profile.StartTime);
                _dicProfile.Remove(profile.StartTime);
            }
            if (rProfile.PriceList.Count != 0) 
            {
                _lstProfile.Add(rProfile.StartTime);
                _dicProfile.Add(rProfile.StartTime, rProfile);
            }
            _lstProfile.Sort();
        }

        public void Merge(ProfileData profile1, DateTime profileIndex1, ProfileData profile2, DateTime profileIndex2)
        {
            if (profile1.StartTime != profile2.StartTime)
            {
                if (profileIndex1 <= profileIndex2)
                {
                    MergeAction(profile1, profileIndex1, profile2, profileIndex2);
                }
                else
                {
                    MergeAction(profile2, profileIndex2, profile1, profileIndex1);
                }
            }
        }

        private void MergeAction(ProfileData profile1, DateTime profileIndex1, ProfileData profile2, DateTime profileIndex2) {
            ProfileData rProfile1 = _dicProfile[profile1.StartTime].Split(profileIndex1);
            ProfileData rProfile2 = _dicProfile[profile2.StartTime].Split(profileIndex2);
            List<DateTime> oldPeriodList = new List<DateTime>();
            foreach (DateTime dtKey in _lstProfile)
            {
                oldPeriodList.Add(dtKey);
            }
            foreach (DateTime ProfileKey in oldPeriodList)
            {
                if ((ProfileKey > profile1.StartTime) && (ProfileKey < profile2.StartTime)) {
                    rProfile1.Merge(_dicProfile[ProfileKey]);
                    _lstProfile.Remove(ProfileKey);
                    _dicProfile.Remove(ProfileKey);
                }
            }
            rProfile1.Merge(_dicProfile[profile2.StartTime]);
            if (_dicProfile[profile1.StartTime].PriceList.Count == 0) {
                _lstProfile.Remove(profile1.StartTime);
                _dicProfile.Remove(profile1.StartTime);
            }
            if (rProfile1.PriceList.Count != 0)
            {
                _lstProfile.Add(rProfile1.StartTime);
                _dicProfile.Add(rProfile1.StartTime, rProfile1);
            }
            _lstProfile.Remove(profile2.StartTime);
            _dicProfile.Remove(profile2.StartTime);
            if (rProfile2.PriceList.Count != 0)
            {
                _lstProfile.Add(rProfile2.StartTime);
                _dicProfile.Add(rProfile2.StartTime, rProfile2);
            }
            _lstProfile.Sort();
        }

        public void UpdateOpenClosePrice(List<BarRecord> lstBarRecord)
        {
            List<DateTime> lstProfileBarRecord = new List<DateTime>();
            Dictionary<DateTime, List<BarRecord>> dicBarRecord = new Dictionary<DateTime, List<BarRecord>>();
            foreach (BarRecord barRecord in lstBarRecord)
            {
                //將超出時間的TICK歸回最後一個Period
                barRecord.BarDateTime = barRecord.BarDateTime.AddSeconds(-59);
                if (string.Compare(barRecord.BarDateTime.ToString("HH:mm:ss"), _symSymbolInfo.End) > 0)
                {
                    barRecord.BarDateTime = Convert.ToDateTime(barRecord.BarDateTime.ToString("yyyy/MM/dd") + " " + _symSymbolInfo.End);
                } 
                DateTime dtKey = GetProfileStartTime(barRecord.BarDateTime);
                if (!dicBarRecord.ContainsKey(dtKey))
                {
                    lstProfileBarRecord.Add(dtKey);
                    dicBarRecord.Add(dtKey, new List<BarRecord>());
                }
                dicBarRecord[dtKey].Add(barRecord);
            }
            foreach (DateTime dtKey in lstProfileBarRecord)
            {
                _dicProfile[dtKey].UpdateOpenClosePrice(dicBarRecord[dtKey]);
            }
        }

        private DateTime GetProfileStartTime(DateTime dtKey) {
            DateTime dtResult = DateTime.MinValue;
            for (int intI = 0; intI < _lstProfile.Count; intI++)
            {
                if (_lstProfile[intI] <= dtKey) {
                    dtResult = _lstProfile[intI];
                }
            }
            return dtResult;
        }

        public int POCTPOCount() {
            int intPOCTPOCount = 0;
            foreach (DateTime dtKey in _lstProfile) {
                intPOCTPOCount = intPOCTPOCount + _dicProfile[dtKey].POCTPOCount();
            }
            return intPOCTPOCount + 2;
        }

        public float Highest() {
            float fHighest = -float.MaxValue;
            foreach (DateTime dtKey in _lstProfile)
            {
                fHighest = Math.Max(_dicProfile[dtKey].Highest, fHighest);
            }
            return fHighest;
        }

        public float Lowest()
        {
            float fLowest = float.MaxValue;
            foreach (DateTime dtKey in _lstProfile)
            {
                fLowest = Math.Min(_dicProfile[dtKey].Lowest, fLowest);
            }
            return fLowest;
        }

        public List<float> GetPriceList() {
            List<float> lstPrice = new List<float>();
            try
            {
                foreach (DateTime dtKey in _lstProfile)
                {
                    foreach (float fPrice in _dicProfile[dtKey].PriceList)
                    {
                        if (lstPrice.IndexOf(fPrice) < 0)
                        {
                            lstPrice.Add(fPrice);
                        }
                    }
                }
                lstPrice.Sort();
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message.ToString());
            }
            return lstPrice;
        }

        public List<ProfileData> GetProfileData(DateTime dtStart)
        {
            return GetProfileData(dtStart, dtStart);
        }

        public List<ProfileData> GetProfileData(DateTime dtStart, DateTime dtEnd) {
            List<ProfileData> _resultData = new List<ProfileData>();
            for (int intI = 0; intI < _lstProfile.Count; intI++) {
                if ((_lstProfile[intI] >= dtStart) && (_lstProfile[intI] <= dtEnd)) {
                    _resultData.Add(_dicProfile[_lstProfile[intI]]);
                }
            }
            return _resultData;
        }

        public float GetLastPrice() {
            float _LastPrice = 0;
            if (_lstProfile.Count  > 0) {
                if (_dicProfile[_lstProfile[_lstProfile.Count - 1]].PriceList.Count > 0)
                {
                    _LastPrice = _dicProfile[_lstProfile[_lstProfile.Count - 1]].GetLastPrice();
                }
            }
            return _LastPrice;
        }

        public int GetTotalProfileCount() {
            return _lstProfile.Count();
        }

        public ProfileData GetLastProfile() {
            if (_lstProfile.Count > 0)
            {
                return _dicProfile[_lstProfile[_lstProfile.Count - 1]];
            }
            else {
                return new ProfileData();
            }
        }

        public Dictionary<DateTime,Dictionary<string,int>> GetProfileVT() {
            Dictionary<DateTime, Dictionary<string, int>> Result = new Dictionary<DateTime, Dictionary<string, int>>();
            foreach (DateTime dtKey in _lstProfile) {
                Dictionary<string, int> dicVT = new Dictionary<string, int>();
                dicVT.Add("Volume", (int)_dicProfile[dtKey].Volume);
                dicVT.Add("Ticks", (int)_dicProfile[dtKey].Ticks);
                Result.Add(dtKey, dicVT);
            }
            return Result;
        }

        public void PrintOutAllData() {
            Console.WriteLine(StartTime.ToString());
            Console.WriteLine(EndTime.ToString());
            Console.WriteLine(_symSymbolInfo.SymbolId.ToString());
            Console.WriteLine(_symSymbolInfo.Start);
            Console.WriteLine(_symSymbolInfo.End);

            foreach (DateTime dtProfile in _lstProfile) {
                ProfileData dayProfile = _dicProfile[dtProfile];
                PrintProfileData(dayProfile);
            }
        }

        private void PrintProfileData(ProfileData dayProfile) {
            Console.WriteLine(dayProfile.TimePeriod);
            Console.WriteLine(dayProfile.PriceInterval);
            Console.WriteLine(dayProfile.StartTime);
            Console.WriteLine(dayProfile.EndTime);
            Console.WriteLine(dayProfile.Open);
            Console.WriteLine(dayProfile.Close);
            Console.WriteLine(dayProfile.Highest);
            Console.WriteLine(dayProfile.Lowest);
            Console.WriteLine(dayProfile.IsCombined);
        }

        private void PrintPeriodData(ProfilePeriod PeriodData)
        {
            Console.WriteLine(PeriodData.TimePeriod);
            Console.WriteLine(PeriodData.PriceInterval);
            Console.WriteLine(PeriodData.StartTime);
            Console.WriteLine(PeriodData.EndTime);
            Console.WriteLine(PeriodData.PeriodOpen);
            Console.WriteLine(PeriodData.PeriodClose);
            Console.WriteLine(PeriodData.PeriodHighest);
            Console.WriteLine(PeriodData.PeriodLowest);
            Console.WriteLine(PeriodData.PeriodVolume);
            Console.WriteLine(PeriodData.PeriodTicks);
        }

        public Dictionary<DateTime, List<float>> GetValueAreaList() {
            Dictionary<DateTime, List<float>> ValueAreaList = new Dictionary<DateTime, List<float>>();
            try
            {
                for (int i = 0; i < _lstProfile.Count; i++) {
                    ValueAreaList.Add(_lstProfile[i], _dicProfile[_lstProfile[i]].GetProfileValueArea());
                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message.ToString());
            }
            return ValueAreaList;
        }

        private void CalculateData() {
            CalculateCellPrice();
            CalculateVTData();
        }

        private void CalculateCellPrice() {
            AxisPriceHeight = Highest();
            AxisPriceLow = Lowest();
            _lstCellPrice.Clear();
            _dicProfileData.Clear();
            _dicVolData.Clear();
            _dicVolSeq.Clear(); 
            _dicTicksData.Clear();
            _dicTicksSeq.Clear();

            // Calculate price range and character range
            AxisPriceHeight = (float)(Math.Ceiling(AxisPriceHeight / _fPriceInterval) * _fPriceInterval) + 2 * _fPriceInterval;
            AxisPriceLow = (float)(Math.Floor(AxisPriceLow / _fPriceInterval) * _fPriceInterval) - 2 * _fPriceInterval;
            for (float fIndex = AxisPriceHeight; fIndex >= AxisPriceLow; fIndex -= _fPriceInterval)
            {
                _dicProfileData.Add(fIndex, new Dictionary<float, ProfileCharData>());
                _dicVolData.Add(fIndex, new Dictionary<int, float>());
                _dicTicksData.Add(fIndex, new Dictionary<int, float>());
                _lstCellPrice.Add(fIndex);
            }
        }

        private void CalculateVTData()
        {
            Dictionary<float, float> fVolSum = new Dictionary<float, float>();
            Dictionary<float, float> fTicksSum = new Dictionary<float, float>();
            for (int intProfileIndex = 0; intProfileIndex < _lstProfile.Count; intProfileIndex++)
            {
                ProfileData dayProfile = _dicProfile[_lstProfile[intProfileIndex]];
                List<float> lstPrice = dayProfile.PriceList;
                for (int i = 0; i < lstPrice.Count; i++)
                {
                    if (!fVolSum.ContainsKey(lstPrice[i]))
                    {
                        fVolSum.Add(lstPrice[i], 0);
                    }
                    if (!fTicksSum.ContainsKey(lstPrice[i]))
                    {
                        fTicksSum.Add(lstPrice[i], 0);
                    }
                    int iRowIndex = (int)((AxisPriceHeight - lstPrice[i]) / _fPriceInterval);
                    _dicVolData[_lstCellPrice[iRowIndex]].Add(intProfileIndex, dayProfile.VolumeAtPrice(_lstCellPrice[iRowIndex]));
                    _dicTicksData[_lstCellPrice[iRowIndex]].Add(intProfileIndex, dayProfile.TickAtPrice(_lstCellPrice[iRowIndex]));
                    List<float> lstMaxVT = new List<float>();
                    fVolSum[lstPrice[i]] = fVolSum[lstPrice[i]] + dayProfile.VolumeAtPrice(lstPrice[i]);
                    fTicksSum[lstPrice[i]] = fTicksSum[lstPrice[i]] + dayProfile.TickAtPrice(lstPrice[i]);
                }
                float fProfileVolAccMax = 0;
                float fProfileVolAccMaxIndex = -1;
                float fProfileTicksAccMax = 0;
                float fProfileTicksAccMaxIndex = -1;
                foreach (float fKey in fVolSum.Keys)
                {
                    if (fVolSum[fKey] >= fProfileVolAccMax)
                    {
                        fProfileVolAccMax = fVolSum[fKey];
                        fProfileVolAccMaxIndex = fKey;
                    }
                    if (fTicksSum[fKey] >= fProfileTicksAccMax)
                    {
                        fProfileTicksAccMax = fTicksSum[fKey];
                        fProfileTicksAccMaxIndex = fKey;
                    }
                }
                if (!_dicVolSeq.ContainsKey(fProfileVolAccMaxIndex))
                {
                    _dicVolSeq.Add(fProfileVolAccMaxIndex, new List<int>());
                }
                _dicVolSeq[fProfileVolAccMaxIndex].Add(intProfileIndex);
                if (!_dicTicksSeq.ContainsKey(fProfileTicksAccMaxIndex))
                {
                    _dicTicksSeq.Add(fProfileTicksAccMaxIndex, new List<int>());
                }
                _dicTicksSeq[fProfileTicksAccMaxIndex].Add(intProfileIndex);
            }
            foreach (float fIndex in fVolSum.Keys)
            {
                fAxisXVolMax = Math.Max(fVolSum[fIndex], fAxisXVolMax);
                fAxisXVolMin = Math.Min(fVolSum[fIndex], fAxisXVolMin);
                fAxisXTicksMax = Math.Max(fTicksSum[fIndex], fAxisXTicksMax);
                fAxisXTicksMin = Math.Min(fTicksSum[fIndex], fAxisXTicksMin);
            }
        }
    }
}
